/************************************************************************
 * Strathclyde Planning Group, Department of Computer and Information Sciences,
 * University of Strathclyde, Glasgow, UK http://planning.cis.strath.ac.uk/
 * 
 * Copyright 2007, Keith Halsey Copyright 2008, Andrew Coles and Amanda Smith
 * 
 * (Questions/bug reports now to be sent to Andrew Coles)
 * 
 * This file is part of JavaFF.
 * 
 * JavaFF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 2 of the License, or (at your option) any later
 * version.
 * 
 * JavaFF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * JavaFF. If not, see <http://www.gnu.org/licenses/>.
 * 
 ************************************************************************/

package javaff;

import java.io.File;
import java.io.PrintStream;
import java.math.BigDecimal;

import javaff.data.GroundProblem;
import javaff.data.Plan;
import javaff.data.TimeStampedPlan;
import javaff.data.TotalOrderPlan;
import javaff.data.UngroundProblem;
import javaff.parser.PDDL21parser;
import javaff.planning.FFState;
import javaff.planning.NullFilter;
import javaff.planning.TemporalMetricState;
import javaff.scheduling.JavaFFScheduler;
import javaff.scheduling.Scheduler;
import javaff.search.BestFirstSearch;
import javaff.search.HValueComparator;

public class JavaFF{
	public static BigDecimal EPSILON = new BigDecimal(0.01);
	public static BigDecimal MAX_DURATION = new BigDecimal("100000"); // maximum

	public static PrintStream planOutput = System.out;
	public static PrintStream parsingOutput = System.out;
	public static PrintStream infoOutput = System.out;
	public static PrintStream errorOutput = System.err;

	public static void main(String args[]) {
		EPSILON = EPSILON.setScale(2, BigDecimal.ROUND_HALF_EVEN);
		MAX_DURATION = MAX_DURATION.setScale(2, BigDecimal.ROUND_HALF_EVEN);

		if (args.length < 2) {
			System.out
			        .println("Parameters needed: domainFile.pddl problemFile.pddl [random seed] [outputfile.sol");

		} else {
			File domainFile = new File(args[0]);
			File problemFile = new File(args[1]);
			plan(domainFile, problemFile);
		}
	}

	public static Plan plan(File dFile, File pFile) {
		// ********************************
		// Parse and Ground the Problem
		// ********************************
		long startTime = now();

		UngroundProblem unground = PDDL21parser.parseFiles(dFile, pFile);

		if (unground == null) {
			throw new IllegalArgumentException(
			        "Parsing error - see console for details");
		}

		final GroundProblem ground = unground.ground();

		long afterGrounding = now();

		// ********************************
		// Search for a plan
		// ********************************

		// Get the initial state
		final TemporalMetricState initialState = ground
		        .getTemporalMetricInitialState();
		final FFState goalState = new BestFirstSearch(initialState, NullFilter
		        .getInstance(), new HValueComparator()).search();
		final long afterPlanning = now();
		TimeStampedPlan tsp = null;
		if (goalState != null) {
			infoOutput.println("Scheduling");

			Scheduler scheduler = new JavaFFScheduler(ground);
			tsp = scheduler.schedule((TotalOrderPlan) goalState.getSolution());
			if (tsp != null) {
				tsp.print(planOutput);
			}

		}

		double groundingTime = (afterGrounding - startTime) / 1000.00;
		double planningTime = (afterPlanning - afterGrounding) / 1000.00;
		double schedulingTime = (now() - afterPlanning) / 1000.00;

		infoOutput.println("Instantiation Time =\t\t" + groundingTime + "sec");
		infoOutput.println("Planning Time =\t" + planningTime + "sec");
		infoOutput.println("Scheduling Time =\t" + schedulingTime + "sec");

		return tsp;
	}

	private static long now() {
		return System.currentTimeMillis();
	}

}
